package com.example.study.config.shiro;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.springframework.messaging.handler.HandlerMethod;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.servlet.support.RequestContextUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.Map;

@Slf4j
public class AuthFilter extends AuthenticatingFilter {

    // 定义jackson对象
    private static final ObjectMapper MAPPER = new ObjectMapper();


    /**
     * 步骤1.所有请求全部拒绝访问
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        HttpServletRequest httpRequest = (HttpServletRequest) request;

        if (httpRequest.getMethod().equals(RequestMethod.OPTIONS.name()) || httpRequest.getRequestURI().equals("/favicon.ico")) {
            return true;
        }

        /******注解调用如下******/
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        WebApplicationContext ctx = RequestContextUtils.findWebApplicationContext(httpServletRequest);
        RequestMappingHandlerMapping mapping = ctx.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class);
        HandlerExecutionChain handler = null;
        try {
            handler = mapping.getHandler(httpServletRequest);
            Annotation[] declaredAnnotations = ((HandlerMethod) handler.getHandler()).getMethod().getDeclaredAnnotations();
            if (declaredAnnotations.length != 0) {
                for (Annotation annotation : declaredAnnotations) {
                    /**
                     *如果含有注解，则认为是不需要验证是否登录，
                     *直接放行即可
                     **/
                    if (NoAuthentication.class.equals(annotation.annotationType())) {
                        return true;
                    }
                }
            }
            /*******注解调用如上*******/
//————————————————
//            版权声明：本文为CSDN博主「Acerola-」的原创文章，遵循CC 4.0 BY-SA版权协议，转载请附上原文出处链接及本声明。
//            原文链接：https://blog.csdn.net/mqq2502513332/article/details/121890778

            return false;
        } catch (Exception e) {
            return false;
        }


    }
        /**
         * 步骤2，拒绝访问的请求，会调用onAccessDenied方法，
         * onAccessDenied方法先获取 token，再调用executeLogin方法
         */
        @Override
        protected boolean onAccessDenied (ServletRequest servletRequest, ServletResponse servletResponse) throws
        Exception {
            HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
            //获取请求token，如果token不存在，直接返回
            //从header中获取token
            String token = httpRequest.getHeader("token");
            //如果header中不存在token，则从参数中获取token
            if (StringUtils.isBlank(token)) {
                token = httpRequest.getParameter("token");
            }
            if (StringUtils.isBlank(token)) {
                HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
                httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
                httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtil.getOrigin());
                httpResponse.setCharacterEncoding("UTF-8");
                Map<String, Object> result = new HashMap<>();
                result.put("status", 400);
                result.put("msg", "请先登录");
                log.error("请先登录" + "   httpRequest:" + httpRequest.getRequestURI() + "      " + httpRequest.getMethod());
                String json = MAPPER.writeValueAsString(result);
                httpResponse.getWriter().print(json);
                return false;
            }
            return executeLogin(servletRequest, servletResponse);
        }

        /**
         * token失效时候调用
         */
        @Override
        protected boolean onLoginFailure (AuthenticationToken token, AuthenticationException e, ServletRequest
        request, ServletResponse response){
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            httpResponse.setContentType("application/json;charset=utf-8");
            httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
            httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtil.getOrigin());
            httpResponse.setCharacterEncoding("UTF-8");
            try {
                //处理登录失败的异常
                Throwable throwable = e.getCause() == null ? e : e.getCause();
                Map<String, Object> result = new HashMap<>();
                result.put("status", 400);
                result.put("msg", "登录凭证已失效，请重新登录: " + e.getMessage());
                result.put("token", token);
                log.error("登录凭证已失效，请重新登录: " + e.getMessage() + "\ntoken: " + token);
                String json = MAPPER.writeValueAsString(result);
                httpResponse.getWriter().print(json);
            } catch (IOException e1) {
            }
            return false;
        }


        @Override
        protected AuthenticationToken createToken (ServletRequest servletRequest, ServletResponse servletResponse) throws
        Exception {
            HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
            //从header中获取token
            String token = httpRequest.getHeader("token");
            //如果header中不存在token，则从参数中获取token
            if (StringUtils.isBlank(token)) {
                token = httpRequest.getParameter("token");
            }
            return new AuthToken(token);
        }
    }
